﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using System.IO;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text.Editor;
using System.Threading;
using System.Windows.Threading;
using System.Collections.ObjectModel;
using Microsoft.VisualStudio.Text;
using Util;
using Logging;
using Microsoft.VisualStudio.Text.Operations;
using System.Runtime.CompilerServices;

namespace UWCSE.CompleterVSPackage
{
    public partial class CompletionsControl : UserControl
    {
        private readonly CompletionsToolWindow parent;
        private readonly ObjectBox<IWpfTextView> activeWpfTextViewBox = new ObjectBox<IWpfTextView>();

        private bool dontHide = false;

        private readonly ProcessHandler processHandler;
        private readonly CompletionsGuiHelper guiHelper;

        public CompletionsControl(CompletionsToolWindow parent)
        {
            InitializeComponent();

            this.parent = parent;
            this.processHandler = new ProcessHandler();
            this.guiHelper = new GuiHelper(processHandler, resultsList, activeWpfTextViewBox, this);
            this.resultsList.ItemsSource = guiHelper.Results;
        }

        private class GuiHelper : CompletionsGuiHelper
        {
            private readonly CompletionsControl cc;

            internal GuiHelper(ProcessHandler processHandler, ListBox resultsList,
                               ObjectBox<IWpfTextView> activeWpfTextViewBox,
                               CompletionsControl cc)
                : base(processHandler, resultsList, activeWpfTextViewBox)
            {
                this.cc = cc;
            }

            internal override void Show()
            {
                    cc.DoShow();
                    Logger.PrintLineWithTime("Window visible");
                cc.dontHide = false;
            }

            internal override void BeforeShow()
            {
                cc.dontHide = true;
            }

            internal override void NotPartialExpression()
            {
                cc.DoHide();
            }

            internal override Span HandleRewriteInfo(Span matchSpan, string matchText, string rewrite)
            {
                if (!rewrite.Equals(matchText))
                {
                    var undoHist = processHandler.CurrentUndoHistory;
                    ITextUndoTransaction transaction = null;
                    if (undoHist != null)
                    {
                        transaction = undoHist.CreateTransaction(@"Add ""Unknown"" to partial expression");
                    }
                    else
                    {
                        LogLineAboutCompletion("FAILED TO TRACK UNDO for REWRITE");
                    }
                    activeWpfTextViewBox.Contents.TextBuffer.Replace(matchSpan, rewrite);
                    if (transaction != null)
                    {
                        transaction.AddUndo(new LoggingUndoPrimitive(
                            "Rewrite for completion " + this.CompletionsCancelToken));
                        transaction.Complete();
                    }

                    LogLineAboutCompletion("PRE-REWRITE MATCH: " + matchText.Escape());
                }
                return new Span(matchSpan.Start, rewrite.Length);
            }

            internal override void FinishedReplace()
            {
                cc.DoHide();
            }

            internal override void NotifyMatchType(string typeStr)
            {
                // Do nothing.
            }
        }

        private IVsWindowFrame GetWindow()
        {
            var window = (ToolWindowPane)parent.GetIVsWindowPane();
            return (IVsWindowFrame)window.Frame;
        }

        private void DoShow()
        {
            var window = GetWindow();

            var textViewOrigin = (activeWpfTextViewBox.Contents as UIElement).PointToScreen(new Point(0, 0));

            var caretPos = activeWpfTextViewBox.Contents.Caret.Position.BufferPosition;
            var charBounds = activeWpfTextViewBox.Contents
                .GetTextViewLineContainingBufferPosition(caretPos)
                .GetCharacterBounds(caretPos);
            double textBottom = charBounds.Bottom;
            double textX = charBounds.Right;

            Guid guid = default(Guid);
            double newLeft = textViewOrigin.X + textX - activeWpfTextViewBox.Contents.ViewportLeft;
            double newTop = textViewOrigin.Y + textBottom - activeWpfTextViewBox.Contents.ViewportTop;
            window.SetFramePos(VSSETFRAMEPOS.SFP_fMove, ref guid,
                (int)newLeft, (int)newTop,
                0, 0);

            window.Show();
            resultsList.Focus();
        }
        private void DoHide()
        {
            var window = GetWindow();
            window.Hide();

            guiHelper.CancelCompletion();
        }

        private void resultsList_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            guiHelper.DoReplace();
        }

        private void resultsList_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            if (!this.IsFocused && !this.IsKeyboardFocusWithin && !dontHide)
            {
                guiHelper.LogLineAboutCompletion("CLOSING WINDOW: Lost keyboard focus");
                DoHide();
            }
        }

        private void resultsList_ScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            guiHelper.resultsList_ScrollChanged();
        }

        private void resultsList_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyboardDevice.Modifiers.Equals(ModifierKeys.None))
            {
                if (e.Key.Equals(Key.Enter))
                {
                    guiHelper.DoReplace();
                }
                else if (e.Key.Equals(Key.Escape))
                {
                    guiHelper.LogLineAboutCompletion("CLOSING WINDOW: User hit Escape");
                    DoHide();
                }
            }
        }

        internal void ShowQueryWindow()
        {
            // TODO ?
            new QueryControl(processHandler, activeWpfTextViewBox)
                .DoCompletionAction();
        }

        internal void DoCompletionAction()
        {
            guiHelper.DoCompletionAction();
        }
        public bool IsReady { get { return guiHelper.IsReady; } }
    }
}